package com.example.OnlineStorage.util;

import com.example.OnlineStorage.enums.GlobalVariables_String;
import com.example.OnlineStorage.pojo.Response.ResponseData;
import org.apache.tika.Tika;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigInteger;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;

import static com.example.OnlineStorage.util.Verify.VerifyFileType_Image;
import static com.example.OnlineStorage.util.Verify.VerifyFileType_Video;

/**
 * IO操作工具类，提供文件读写、合并、删除以及类型识别等功能。
 */
@Component
public class IO {
	
	// 定义最大块大小，单位为字节
	private static final long MaxChunkSize = 4 * 1024 * 1024;
	
	/**
	 * 初始化文件，检查文件是否存在以及起始位置是否合理。
	 *
	 * @param filePath 文件路径
	 * @param start    起始读取位置
	 * @throws RuntimeException 如果文件不存在或起始位置超出文件长度
	 */
	private static void init (String filePath, long start) {
		File file = new File(filePath);
		if (! file.exists()) {
			throw new RuntimeException("资源不存在.");
		}
		if (start >= file.length()) {
			throw new RuntimeException("请求异常.");
		}
	}
	
	/**
	 * 在线读取文件，返回文件的一个片段。
	 *
	 * @param filePath 文件路径
	 * @param start    起始读取位置
	 * @return 包含文件片段的BinBody对象
	 */
	public static ResponseData onlineReadFile (String filePath, long start, String Range) {
		init(filePath, start);
		String type = getFileType(filePath);
		boolean isImag = VerifyFileType_Image(type);
		boolean isVideo = VerifyFileType_Video(type);
		File file = new File(filePath);
		if(isImag&&file.length()>MaxChunkSize){
			filePath=GlobalVariables_String.File_Path_Files.getValue()+GlobalVariables_String.File_Limit_Exceeded.getValue();
			type = getFileType(filePath);
		}
		if(!isImag&&!isVideo){
			filePath=GlobalVariables_String.File_Path_Files.getValue()+GlobalVariables_String.Not_Supported.getValue();
			type = getFileType(filePath);
		}
		return new ResponseData(filePath, null, type, Range, MaxChunkSize);
	}
	
	/**
	 * 下载文件，返回完整的文件。
	 *
	 * @param filePath 文件路径
	 * @param name     文件名
	 * @param start    起始读取位置
	 * @return 包含完整文件的BinBody对象
	 */
	public static ResponseData DownloadFile (String filePath, String name, long start, String Range) {
		init(filePath, start);
		String type = getFileType(filePath);
		return new ResponseData(filePath, name, type, Range, 0);
	}
	
	/**
	 * 检查文件是否存在。
	 *
	 * @param filePath 文件路径
	 * @return 如果文件存在返回true，否则返回false
	 */
	public static boolean isFileExist (String filePath) {
		File file = new File(filePath);
		return file.exists();
	}
	
	
	/**
	 * 获取文件大小
	 * @param filePath 文件路径
	 * @return 文件大小
	 */
	public static BigInteger getFileSize (String filePath) {
		File file = new File(filePath);
		return BigInteger.valueOf(file.length());
	}
	
	/**
	 * 写入文件，从上传的部分文件中传输数据到指定路径。
	 *
	 * @param file     上传的多部分文件
	 * @param filePath 写入的目标文件路径
	 */
	public static void writeFile (MultipartFile file, String filePath) {
		try {
			file.transferTo(new File(filePath));
		} catch (IOException e) {
			throw new RuntimeException("文件块上传失败");
		}
	}
	
	
	
	/**
	 * 合并文件，将多个分片文件合并到指定路径。
	 *
	 * @param tempPath     临时文件路径
	 * @param tempNames    临时文件名数组
	 * @param writePath    写入的目标文件路径
	 * @return 合并后文件的类型
	 */
	public static String mergeFile (String tempPath, List<String> tempNames, String writePath) {
		try {
			Path path1 = Paths.get(writePath);
			try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(path1, StandardOpenOption.CREATE, StandardOpenOption.WRITE))) {
				for (String fileName : tempNames) {
					File partFile = new File(tempPath + fileName);
					Files.copy(partFile.toPath(), out);
				}
			}
			
			return IO.getFileType(writePath);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
	
	
	/**
	 * 创建文件夹，如果文件夹已存在则抛出异常。
	 *
	 * @param filePath 文件夹路径
	 */
	public static void createFolder (String filePath) {
		try {
			File file = new File(filePath);
			if (! file.exists()) {
				if (! file.mkdirs()) {
					throw new RuntimeException("文件夹创建失败");
				}
				return;
			}
			throw new RuntimeException("文件夹已存在");
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		
	}
	
	/**
	 * 删除文件。
	 *
	 * @param filePath 文件路径
	 */
	public static void deleteFile (String filePath) {
		File file = new File(filePath);
		if (file.exists()) {
			if (! file.delete()) {
				throw new RuntimeException("文件删除失败");
			}
		}
		
	}
	
	
	/**
	 * 删除临时文件夹及其内容。
	 *
	 * @param filePath 临时文件夹路径
	 */
	public static void deleteTempFolder (String filePath) {
		try {
			File file = new File(filePath);
			if (file.exists()) {
				File[] files = file.listFiles();
				if (files != null) {
					for (File f : files) {
						if (! f.delete()) {
							throw new RuntimeException("删除临时文件夹中的文件碎片失败，文件夹路径：" + filePath);
						}
					}
				}
				if (! file.delete()) {
					throw new RuntimeException("删除临时文件夹失败，文件夹路径：" + filePath);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
	
	
	/**
	 * 通过文件路径获取文件类型。
	 *
	 * @param filePath 文件路径
	 * @return 文件类型
	 */
	public static String getFileType (String filePath) {
		try (FileInputStream inputStream = new FileInputStream(filePath)) {
			Tika tika = new Tika();
			return tika.detect(inputStream);
		} catch (Exception ex) {
			throw new RuntimeException("文件类型异常.");
		}
	}
	
	/**
	 * 通过文件获取文件类型。
	 *
	 * @param file 文件
	 * @return 文件类型
	 */
	public static String getFileType (MultipartFile file) {
		try (InputStream inputStream = file.getInputStream()) {
			Tika tika = new Tika();
			return tika.detect(inputStream);
		} catch (Exception ex) {
			throw new RuntimeException("文件类型检测时发生异常.");
		}
	}
}

